//************************************************
//
//! \addtogroup Main_C	 
//! @{
//
//************************************************

#include "hw_ints.h"
#include "hw_memmap.h"
#include "hw_types.h"
#include "adc.h"
#include "debug.h"
#include "gpio.h"
#include "interrupt.h"
#include "sysctl.h"
#include "timer.h"
#include "uart.h"
#include "dots.h"
#include "intro.h"
#include "random.h"
#include "osram96x16x1.h"
#include <string.h>

#define PUSH_BUTTON GPIO_PIN_4
#define ever (;;)
//************************************************
// Global Variables
//************************************************
unsigned long ADCVal;			///< Stores ADC Value
unsigned long secPlayed = 0;	///< Counts seconds played
unsigned char brdRow[2];		
unsigned long brdPos;			///< Board position
unsigned char btnFlag = 0;		///< Button state flag
extern unsigned long delayTime;	///< Used for program delay

//*****************************************************************************
//
// A bitmap for the board.
//
//     xx
//     xx
//     xx
//     xx
//     xx
//     xx
//     ..
//     ..
//
//*****************************************************************************
//static const unsigned char board[2] =
//{
//   0xFC, 0xFC
//};


//*****************************************************************************
//!
//!	Draw board on LED-Display.
//!
//! Gets a value form the ADC and calculates the position at which the board
//! is showed. Uses one 2-byte unsigned number and divides it to two 8-bit
//! numbers for each row of the display.	
//!
//!	\return None
//*****************************************************************************

void DrawBoard(void)
{
	unsigned short num = 0x3F, hlp;

	// Gets a value from ADC and calculates the position of the board.
	brdPos = ADCVal / 94;

	// Set board position one the screen with two 8-bit numbers
	num <<= brdPos;
	hlp = num & 0xFF00;
	hlp >>= 8;

	brdRow[0] = (unsigned char) num;
	brdRow[1] = (unsigned char) hlp;

	// Display board on the screen.
	OSRAM96x16x1ImageDraw(brdRow, 1, 0, 1, 2); 
	OSRAM96x16x1ImageDraw(brdRow, 2, 0, 1, 2); 
	

}

//*****************************************************************************
//
//! \internal
//! \param *timeStr is pointer to a string that is sent to the PC
//! \param count is the number of characters to be send
//! 
//!	Sends a string to the PC
//!	
//!	Sends a string char by char to the PC via UART.
//! 
//! \return None
//
//*****************************************************************************
static void
UARTSend(const char *timeStr, unsigned long count)
{
    //
    // Loop while there are more characters to send.
    //
    while(count--)
    {
        //
        // Write the next character to the UART.
        //
        UARTCharPutNonBlocking(UART0_BASE, *timeStr++);
    }
}

//*****************************************************************************
//
//! \internal
//! Convert integer time to string format
//!
//!	Using the value of the global variable secPlayed to calculate hours, mins
//! and seconds the game was played. Makes a string hh:mm:ss and sends it via UART. 
//!
//*****************************************************************************
static void time2str(void)
{
	char time[9];
	short min, h;

	// Derive hours and minutes played from the global seconds variable
	h = secPlayed / 3600;
	secPlayed %= 3600;
	min = secPlayed / 60;
	secPlayed %= 60;
	
	// Convert integers to string to be able to be send via UART
	time[0] = h / 10 + '0';
	time[1] = h % 10 + '0';
	time[2] = ':';

	time[3] = min / 10 + '0';
	time[4] = min % 10 + '0';
	time[5] = ':';

	time[6] = secPlayed / 10 + '0';
	time[7] = secPlayed % 10 + '0';
	time[8] = '\0';
	
	// Send time played vie UART
	UARTSend(time, 9);

	// Clear button flag
	btnFlag = 0;
}


//*****************************************************************************
//
//! \internal
//! Actions done after game finishes (misses == 100)
//!
//!	Called after game finishes. Shows information to display giving enough
//! time to the UART to send all characters to the PC.
//!
//! \return None
//*****************************************************************************
static void gameOver(void)
{
	unsigned long i, j;
	delayTime = SysCtlClockGet()/25;
		
	UARTSend("\rPlayed: ", 9);

	// Display some text on the display.
	OSRAM96x16x1Clear();
	OSRAM96x16x1StringDraw("Sending Data ...", 0, 1);
	for (i=0; i < 4; ++i)
	{
		OSRAM96x16x1StringDraw("GAME OVER!", 18, 0);
		for (j = 0; (j < delayTime); ++j);
		OSRAM96x16x1StringDraw("          ", 18, 0);
		for (j = 0; (j < delayTime); ++j);
	}
	
	// Calls function to convert time to string and to send it to PC
	time2str();
}

//*****************************************************************************
//
//! Interrupt handler for Global Timer (Timer0) counting played time.
//!
//! On every second Timer0 interrupts and the handler increments the global
//! variable counting seconds the game is played.
//!
//! \return None
//!
//*****************************************************************************
void timer0IntHandler(void)
{
	TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
	++ secPlayed;
}

//*****************************************************************************
//
//! Interrupt handler for ADC interrupt.
//!
//!	A timer triggered sample of ADC used for calculation of board position and
//!	as data for the random number generating function.
//!
//! \return None
//
//*****************************************************************************
void
ADCIntHandler(void)
{
    unsigned long ulData;
	unsigned long temp;
    // Clear the ADC interrupt.
    ADCIntClear(ADC_BASE, 3);

    // Read the data
    ADCSequenceDataGet(ADC_BASE, 3, &ulData);

    // ADC value filter
    ADCVal = ((ADCVal * 58982) + (ulData * 6554)) / 65536;

	//Add elements to array for random generation
	temp = (ADCVal)? ADCVal : TimerValueGet(TIMER1_BASE,TIMER_A);
	RandomAddEntropy(temp * (TimerValueGet(TIMER0_BASE,TIMER_A)));	
}

//*****************************************************************************
//
//! Interrupt handler for user button.
//!
//!	Sets the button flag to indicate that user-button is pushed.
//!
//! \return None
//
//*****************************************************************************
void buttonIntHandler(void)
{
	GPIOPinIntClear(GPIO_PORTC_BASE, PUSH_BUTTON);
	btnFlag = 1;
}

//*****************************************************************************
//
//! Main function.
//!
//!	Initialises hardware and loop forever while the game is played.
//!
//! \return None
//
//*****************************************************************************
int
main(void)
{

    // Set the clocking to run at 20MHz from the PLL.
    SysCtlClockSet(SYSCTL_SYSDIV_10 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
                   SYSCTL_XTAL_6MHZ);

    // Enable the peripherals used by the application.
    SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

	//Allow interrupts globally
	IntMasterEnable();

    // Configure the ADC to sample the potentiometer when the timer expires.
    ADCSequenceConfigure(ADC_BASE, 3, ADC_TRIGGER_TIMER, 0);
    ADCSequenceStepConfigure(ADC_BASE, 3, 0, ADC_CTL_CH0 | ADC_CTL_IE | ADC_CTL_END);
    ADCSequenceEnable(ADC_BASE, 3);
    ADCIntEnable(ADC_BASE, 3);
    IntEnable(INT_ADC3);

	//Timer interupting @ 1 sec for Global Time measuring
	TimerConfigure(TIMER0_BASE, TIMER_CFG_32_BIT_PER);
    TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet());
	TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
	IntEnable(INT_TIMER0A);
	
    // Set GPIO A0 and A1 as UART pins.
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    // Configure the UART for 115,200, 8-N-1 operation.
    UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 115200,(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
                         UART_CONFIG_PAR_NONE));
	UARTEnable(UART0_BASE);

    // Configure the second timer to generate triggers to the ADC 
    TimerConfigure(TIMER1_BASE, TIMER_CFG_32_BIT_PER);
    TimerLoadSet(TIMER1_BASE, TIMER_A, SysCtlClockGet()/500);
    TimerControlStall(TIMER1_BASE, TIMER_A, true);
    TimerControlTrigger(TIMER1_BASE, TIMER_A, true);
    TimerEnable(TIMER1_BASE, TIMER_A);

	//Configure User Button
	GPIOPinTypeGPIOInput(GPIO_PORTC_BASE, PUSH_BUTTON);
	GPIOIntTypeSet(GPIO_PORTC_BASE, PUSH_BUTTON, GPIO_FALLING_EDGE);
	GPIOPinIntEnable(GPIO_PORTC_BASE, PUSH_BUTTON);
	IntEnable(INT_GPIOC);
			
    // Initialize the OSRAM OLED display.
    OSRAM96x16x1Init();

	//Shows some scrolling info
	intro();

	//Loops forever
    for ever
    {
		//Awaits user button activity
		waitBtnPush();

		//Timer counting global time starts
		TimerEnable(TIMER0_BASE, TIMER_A);

		//Initializes and starts the game
		playGame();

		//The game has finished and the timer is stopped
		TimerDisable(TIMER0_BASE, TIMER_A);

		//Handles acitons needed after game end.
		gameOver();
    }
}	 

//************************************************
//
//	Close Doxygen group	 
//! @}
//
//************************************************
